package com.flex.reflection;

import com.flex.reflection.listener.PropertyListener;
import com.flex.reflection.resolver.ParamterResolveType;
import com.flex.reflection.util.TypeUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 方法元数据
 *
 * @author ganlt
 * @since 2019/09/20 13:04
 */
public class MetaField implements MetaObj,FieldInvoke {
    /**
     * 参数
     */
    private Map<String, Type> paramMap = new HashMap<>();
    /**
     * 定义注解
     */
    private List<Annotation> annotations;
    /**
     * 方法
     */
    private Field field;
    /**
     * 所属类实例
     */
    private Object instance;
    /**
     * 返回类型
     */
    private Type returnType;

    private Map<Annotation, AnnotationEntity> annoEntityObjMap = new HashMap<>();

    private MetaClass.Config config;

    public MetaField(Field field, Object instance) {
        this.field = field;
        this.instance = instance;
        getAnnotationAll();
    }


    public void getAnnotationAll() {
        Annotation[] fieldAnnotations = this.field.getAnnotations();
        Arrays.stream(fieldAnnotations).forEach(annotation -> {
            annoEntityObjMap.put(annotation, new AnnotationEntity(this.config, annotation));
        });

    }

    public Field getField() {
        return field;
    }

    public MetaClass.Config getConfig() {
        return config;
    }

    public void setConfig(MetaClass.Config config) {
        this.config = config;
    }

    @Override
    public AnnotationEntity getAnnotationEntity(Class<? extends Annotation> annotation) {
        Annotation declaredAnnotation = this.field.getDeclaredAnnotation(annotation);
        AnnotationEntity annoEntityObj = new AnnotationEntity(this.config, declaredAnnotation);

        return annoEntityObj;
    }

    @Override
    public boolean hasAnnotation(Class<? extends Annotation> annotation) {
        return field.isAnnotationPresent(annotation);
    }


    @Override
    public List<Type> paramsType() {

        return null;
    }

    @Override
    public Type returnType() {
        return getReturnType();
    }

    @Override
    public Object invoke(Object newVal, PropertyListener propertyListener) throws IllegalAccessException {
        Field field = getField();
        Object target = getInstance();
        Class returnClass = TypeUtils.getFirstTypeClass(returnType());
        Object oldVal = field.get(target);
        Object val = new AtomicReference<>();
        if (propertyListener!=null) {
            val  = propertyListener.writeProperty(field.getName(), oldVal, newVal);
        }

        if (val !=null) {
            try {
                field.set(target, val);
            } catch (IllegalArgumentException | IllegalAccessException e) {
                field.set(target,config.getConvertType().convert(val,returnClass));
            }
        } else {
            try {
                field.set(target, newVal);
            } catch (IllegalArgumentException | IllegalAccessException e) {
                field.set(target,config.getConvertType().convert(newVal,returnClass));
            }
        }
        return null;
    }

    public Type getReturnType() {
        ParamterResolveType paramterResolveType = new ParamterResolveType();
        return paramterResolveType.resolveFieldType(this.field,this.instance.getClass());
    }

    public Object getInstance() {
        return instance;
    }
}
